package com.hhl.library;

import ohos.agp.components.*;
import ohos.agp.database.DataSetSubscriber;
import ohos.app.Context;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * 流式标签布局
 * 原理：重写{@link ComponentContainer onEstimateSize(int, int)}
 * 和{@link ComponentContainer onArrange(int, int, int, int)}
 * 方法
 * Created by HanHailong on 15/10/19.
 */
public class FlowTagLayout extends ComponentContainer implements Component.EstimateSizeListener,
    ComponentContainer.ArrangeListener, Text.TextObserver, Component.FocusChangedListener {

  private static final String TAG = FlowTagLayout.class.getSimpleName();

  /**
   * FlowLayout not support checked.
   */
  public static final int FLOW_TAG_CHECKED_NONE = 0;
  /**
   * FlowLayout support single-select.
   */
  public static final int FLOW_TAG_CHECKED_SINGLE = 1;
  /**
   * FlowLayout support multi-select.
   */
  public static final int FLOW_TAG_CHECKED_MULTI = 2;
  private AdapterDataSetObserver mDataSetObserver;

  /**
   * Should be used by subclasses to listen to changes in the dataset.
   */

  /**
   * The adapter containing the data to be displayed by this view.
   */
  BaseItemProvider mAdapter;

  /**
   * the tag click event callback.
   */
  OnTagClickListener mOnTagClickListener;

  /**
   * the tag select event callback.
   */
  OnTagSelectListener mOnTagSelectListener;

  /**
   * 标签流式布局选中模式，默认是不支持选中的.
   */
  private int mTagCheckMode = FLOW_TAG_CHECKED_NONE;

  /**
   * 存储选中的tag.
   */
  private HashMap mCheckedTagArray = new HashMap();

  public FlowTagLayout(Context context) {
    super(context);
  }

  public FlowTagLayout(Context context, AttrSet attrs) {
    super(context, attrs);
  }

  public FlowTagLayout(Context context, AttrSet attrs, String defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  @Override
  public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
    LogUtil.debug(TAG, " onEstimateSize mAdapter.getCount(): " + mAdapter.getCount());
    //获取Padding
    // 获得它的父容器为它设置的测量模式和大小
    int sizeWidth = EstimateSpec.getSize(widthMeasureSpec);
    int sizeHeight = EstimateSpec.getSize(heightMeasureSpec);
    int modeWidth = EstimateSpec.getMode(widthMeasureSpec);
    int modeHeight = EstimateSpec.getMode(heightMeasureSpec);

    //FlowLayout最终的宽度和高度值
    int resultWidth = 0;
    int resultHeight = 0;

    //测量时每一行的宽度
    int lineWidth = 0;
    //测量时每一行的高度，加起来就是FlowLayout的高度
    int lineHeight = 0;

    //遍历每个子元素
    for (int i = 0, childCount = mAdapter.getCount(); i < childCount; i++) {
      Component childView = getComponentAt(i);
      //测量每一个子view的宽和高
      measureChild(childView, widthMeasureSpec, heightMeasureSpec);

      //获取到测量的宽和高
      int childWidth = childView.getEstimatedWidth();
      int childHeight = childView.getEstimatedHeight();

      //因为子View可能设置margin，这里要加上margin的距离
      int realChildWidth = childWidth + childView.getMarginLeft() + childView.getMarginRight();
      int realChildHeight = childHeight + childView.getMarginTop() + childView.getMarginBottom();

      LogUtil.debug(TAG, " realChildWidth: " + realChildWidth);
      LogUtil.debug(TAG, " realChildHeight: " + realChildHeight);
      //如果当前一行的宽度加上要加入的子view的宽度大于父容器给的宽度，就换行
      if ((lineWidth + realChildWidth) > sizeWidth) {
        //换行
        resultWidth = Math.max(lineWidth, realChildWidth);
        resultHeight += realChildHeight;
        //换行了，lineWidth和lineHeight重新算
        lineWidth = realChildWidth;
        lineHeight = realChildHeight;
      } else {
        //不换行，直接相加
        lineWidth += realChildWidth;
        //每一行的高度取二者最大值
        lineHeight = Math.max(lineHeight, realChildHeight);
      }

      //遍历到最后一个的时候，肯定走的是不换行
      if (i == childCount - 1) {
        resultWidth = Math.max(lineWidth, resultWidth);
        resultHeight += lineHeight;
      }

      LogUtil.debug(TAG, " resultWidth: " + resultWidth);
      LogUtil.debug(TAG, " resultHeight: " + resultHeight);
      setEstimatedSize(EstimateSpec.getChildSizeWithMode(modeWidth == EstimateSpec.PRECISE
          ? sizeWidth : resultWidth, widthMeasureSpec, EstimateSpec.NOT_EXCEED),
          EstimateSpec.getChildSizeWithMode(modeHeight == EstimateSpec.PRECISE
          ? sizeHeight : resultHeight, heightMeasureSpec, EstimateSpec.NOT_EXCEED));

    }
    return true;
  }

  private void measureChild(Component child, int parentWidthMeasureSpec,
                              int parentHeightMeasureSpec) {
    LayoutConfig lc = child.getLayoutConfig();
    int childWidthMeasureSpec =
        EstimateSpec.getChildSizeWithMode(lc.width, parentWidthMeasureSpec,
          EstimateSpec.NOT_EXCEED);
    int childHeightMeasureSpec =
        EstimateSpec.getChildSizeWithMode(lc.height, parentHeightMeasureSpec,
          EstimateSpec.NOT_EXCEED);
    child.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec);
  }

  @Override
  public boolean onArrange(int l, int t, int r, int b) {

    LogUtil.debug(TAG, " onArrange mAdapter.getCount(): " + mAdapter.getCount());
    int flowWidth = getWidth();

    int childLeft = 0;
    int childTop = 0;

    //遍历子控件，记录每个子view的位置
    for (int i = 0, childCount = mAdapter.getCount(); i < childCount; i++) {
      Component childView = getComponentAt(i);

      //跳过View.GONE的子View
      if (childView.getVisibility() == Component.HIDE) {
        continue;
      }

      //获取到测量的宽和高
      int childWidth = childView.getEstimatedWidth();
      int childHeight = childView.getEstimatedHeight();

      //因为子View可能设置margin，这里要加上margin的距离
      if (childLeft + childView.getMarginLeft() + childWidth
          + childView.getMarginRight() > flowWidth) {
        //换行处理
        childTop += (childView.getMarginTop() + childHeight + childView.getMarginBottom());
        childLeft = 0;
      }
      //布局
      int left = childLeft + childView.getMarginLeft();
      int top = childTop + childView.getMarginTop();
      childView.arrange(left, top, childWidth, childHeight);
      LogUtil.debug(TAG, " left: " + left);
      LogUtil.debug(TAG, " top: " + top);
      LogUtil.debug(TAG, " childWidth: " + childWidth);
      LogUtil.debug(TAG, " childHeight: " + childHeight);
      childLeft += (childView.getMarginLeft() + childWidth + childView.getMarginRight());
    }
    return true;
  }

  @Override
  public void onFocusChange(Component component, boolean b) {

  }

  @Override
  public void onTextUpdated(String s, int i, int i1, int i2) {

  }

  class AdapterDataSetObserver extends DataSetSubscriber {
    @Override
    public void onChanged() {
      super.onChanged();
      reloadData();
    }
  }


  /**
   * 重新加载刷新数据.
   */
  private void reloadData() {
    removeAllComponents();

    boolean isSetted = false;
    for (int i = 0; i < mAdapter.getCount(); i++) {
      final int j = i;
      mCheckedTagArray.put(i, false);
      final Component childView = mAdapter.getComponent(i, null, this);
      addComponent(childView, i);

      if (mAdapter instanceof OnInitSelectedPosition) {
        boolean isSelected = ((OnInitSelectedPosition) mAdapter).isSelectedPosition(i);
        //判断一下模式
        if (mTagCheckMode == FLOW_TAG_CHECKED_SINGLE) {
          //单选只有第一个起作用
          if (isSelected && !isSetted) {
            mCheckedTagArray.put(i, true);
            childView.setSelected(true);
            isSetted = true;
            if (mOnTagSelectListener != null) {
              mOnTagSelectListener.onItemSelect(FlowTagLayout.this, Arrays.asList(j));
            }
          }
        } else if (mTagCheckMode == FLOW_TAG_CHECKED_MULTI) {
          if (isSelected) {
            mCheckedTagArray.put(i, true);
            childView.setSelected(true);
          }
        }
      }

      childView.setClickedListener(new ClickedListener() {
        @Override
        public void onClick(Component component) {
          if (mTagCheckMode == FLOW_TAG_CHECKED_NONE) {
            if (mOnTagClickListener != null) {
              mOnTagClickListener.onItemClick(FlowTagLayout.this, childView, j);
            }
          } else if (mTagCheckMode == FLOW_TAG_CHECKED_SINGLE) {
            //判断状态
            if ((boolean) mCheckedTagArray.get(j)) {
              mCheckedTagArray.put(j, false);
              childView.setSelected(false);
              if (mOnTagSelectListener != null) {
                mOnTagSelectListener.onItemSelect(FlowTagLayout.this, new ArrayList<Integer>());
              }
              return;
            }

            for (int k = 0; k < mAdapter.getCount(); k++) {
              mCheckedTagArray.put(k, false);
              getComponentAt(k).setSelected(false);
            }
            mCheckedTagArray.put(j, true);
            childView.setSelected(true);

            if (mOnTagSelectListener != null) {
              mOnTagSelectListener.onItemSelect(FlowTagLayout.this, Arrays.asList(j));
            }
          } else if (mTagCheckMode == FLOW_TAG_CHECKED_MULTI) {
            if ((boolean) mCheckedTagArray.get(j)) {
              mCheckedTagArray.put(j, false);
              childView.setSelected(false);
            } else {
              mCheckedTagArray.put(j, true);
              childView.setSelected(true);
            }
            //回调
            if (mOnTagSelectListener != null) {
              List<Integer> list = new ArrayList<Integer>();
              for (int k = 0; k < mAdapter.getCount(); k++) {
                if ((boolean) mCheckedTagArray.get(k)) {
                  list.add(k);
                }
              }
              mOnTagSelectListener.onItemSelect(FlowTagLayout.this, list);
            }
          }
        }
      });
    }
    //回调
    if (mOnTagSelectListener != null) {
      List<Integer> list = new ArrayList<Integer>();
      for (int k = 0; k < mAdapter.getCount(); k++) {
        if ((boolean) mCheckedTagArray.get(k)) {
          list.add(k);
        }
      }
      mOnTagSelectListener.onItemSelect(FlowTagLayout.this, list);
    }
  }

  /**
   * 清除所有被选择的选项.
   *
   * @author https://github.com/wanyt.
   * @time 2016年11月13日16:07:23.
   */
  public void clearAllOption() {
    for (int i = 0; i < mAdapter.getCount(); i++) {
      if ((boolean) mCheckedTagArray.get(i)) {
        LogUtil.debug(" mCheckedTagArray： ", "" + mCheckedTagArray.get(i));
        getComponentAt(i).setSelected(false);
        mCheckedTagArray.replace(i, false);
      }
    }
  }

  public void setOnTagClickListener(OnTagClickListener onTagClickListener) {
    this.mOnTagClickListener = onTagClickListener;
  }

  public void setOnTagSelectListener(OnTagSelectListener onTagSelectListener) {
    this.mOnTagSelectListener = onTagSelectListener;
  }

  /**
   * 像ListView、GridView一样使用FlowLayout.
   *
   * @param adapter .
   */
  public void setAdapter(BaseItemProvider adapter) {
    if (mAdapter != null && mDataSetObserver != null) {
      mAdapter.removeDataSubscriber(mDataSetObserver);
    }

    //清除现有的数据
    removeAllComponents();
    mAdapter = adapter;

    if (mAdapter != null) {
      setEstimateSizeListener(this);
      setArrangeListener(this);
      mDataSetObserver = new AdapterDataSetObserver();
      mAdapter.addDataSubscriber(mDataSetObserver);
    }
  }

  public BaseItemProvider getAdapter() {
    return mAdapter;
  }

  /**
   * 获取标签模式.
   *
   * @return.
   */
  public int getmTagCheckMode() {
    return mTagCheckMode;
  }

  /**
   * 设置标签选中模式.
   *
   * @param tagMode .
   */
  public void setTagCheckedMode(int tagMode) {
    this.mTagCheckMode = tagMode;
  }
}
